package com.avengers.core.kit;

import com.avengers.core.bean.enums.Charset;
import com.avengers.core.bean.enums.ForwardWay;
import com.avengers.core.bean.enums.HttpMethod;
import com.jfinal.kit.Ret;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yu.wang
 * @since 2020-06-03 17:16
 **/
public class HttpForwardKit {
    private static final Logger logger = LoggerFactory.getLogger(HttpForwardKit.class);
    private static final String IP_DEFAULT = "123.56.122.243";
    private static final String IP_EXTERNAL = "8.210.157.232";
    private static final String CLIENT_ID = "avengers";
    private static final String CLIENT_KEY = "5dd5c10d252e0fc8c41ba928286aa846";
    private final String ip;
    private final String way;

    public static final HttpForwardKit DEFAULT = new HttpForwardKit(IP_DEFAULT);
    public static final HttpForwardKit FORWARD_EXTERNAL = new HttpForwardKit(IP_EXTERNAL);

    private HttpForwardKit(String ip) {
        this(ip, null);
    }

    private HttpForwardKit(String ip, ForwardWay way) {
        this.ip = ip;
        this.way = null == way ? null : way.name();
    }

    public static HttpForwardKit of(String ip, ForwardWay way) {
        return new HttpForwardKit(ip, way);
    }

    public String send(String url, String postStr) {
        return send(HttpMethod.POST, url, postStr);
    }

    public String send(String url, String postStr, Map<String, String> headers) {
        return send(HttpMethod.POST, url, postStr, headers);
    }

    public String send(HttpMethod method, String url, String postStr, boolean urlEncode) {
        return send(method, url, postStr, Charset.UTF8, urlEncode, false);
    }

    public String send(HttpMethod method, String url, String postStr, boolean urlEncode, Map<String, String> headers) {
        return send(method, url, postStr, Charset.UTF8, urlEncode, false, headers);
    }

    public String send(HttpMethod method, String url, String postStr, boolean urlEncode, boolean postEncode) {
        return send(method, url, postStr, Charset.UTF8, urlEncode, postEncode);
    }

    public String send(HttpMethod method, String url, String postStr, boolean urlEncode, boolean postEncode, Map<String, String> headers) {
        return send(method, url, postStr, Charset.UTF8, urlEncode, postEncode, headers);
    }

    public String send(HttpMethod method, String url, String postStr, Map<String, String> headers) {
        return send(method, url, postStr, Charset.UTF8, false, false, headers);
    }

    public String send(HttpMethod method, String url, String postStr) {
        return send(method, url, postStr, Charset.UTF8, false, false);
    }

    public String send(HttpMethod method, String url, String postStr, Charset charset, Map<String, String> headers) {
        return send(method, url, postStr, charset, false, false, headers);
    }

    public String send(HttpMethod method, String url, String postStr, Charset charset) {
        return send(method, url, postStr, charset, false, false);
    }

    public String send(HttpMethod method, String url, String postStr, Charset charset, boolean urlEncode, boolean postEncode) {
        return send(method, url, postStr, charset, urlEncode, postEncode, null);
    }

    public String send(HttpMethod method, String url, String postStr, Charset charset, boolean urlEncode, boolean postEncode, Map<String, String> headers) {
        try {
            Map<String, String> params = new HashMap<>(16);
            params.put("client_id", CLIENT_ID);
            params.put("u", EncryptKit.urlEncode(url));
            params.put("m", method.name());
            params.put("c", charset.value);
            params.put("e", String.valueOf(urlEncode));
            params.put("pe", String.valueOf(postEncode));
            if (StringKit.notBlank(way)) {
                params.put("w", way);
            }
            if (StringKit.notBlank(postStr)) {
                params.put("p", EncryptKit.urlEncode(postStr));
            }
            if (null != headers && !headers.isEmpty()) {
                params.put("h", EncryptKit.urlEncode(JsonKit.toGson(headers)));
            }
            String sign = EncryptKit.sign(params, CLIENT_KEY);
            params.put("sign", sign);
            return sendByJsoup(StringKit.mapToHttpParams(params));
        } catch (Exception e) {
            logger.error("error : {}", e.getMessage(), e);
        }
        return null;
    }

    private String getUrl() {
        return "http://" + this.ip + ":32970/forward";
    }

    private String sendByJsoup(String content) throws Exception {
        Connection connection = Jsoup.connect(getUrl());
        connection.ignoreContentType(true);
        connection.ignoreHttpErrors(true);
        connection.followRedirects(true);
        connection.postDataCharset(Charset.UTF8.value);
        connection.method(Connection.Method.POST);
        connection.timeout(120000);
        connection.requestBody(content);
        Connection.Response response = connection.execute();
        String result = response.body().trim();
        Ret ret = JsonKit.fromJson(result, Ret.class);
        if (ret.isOk()) {
            return URLDecoder.decode(ret.getStr("result"), Charset.UTF8.value);
        } else {
            logger.error("api request forward by Jsoup result: {}", result);
            throw new RuntimeException(ret.getStr("msg"));
        }
    }

    private String sendByUrlConnection(String content) throws Exception {
        HttpURLConnection urlConnection = null;
        BufferedReader reader = null;
        try {
            urlConnection = testCreateConnection(content);
            urlConnection.connect();
            reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), Charset.UTF8.value));
            StringBuilder responseBuilder = new StringBuilder();
            String line = reader.readLine();

            while (null != line) {
                responseBuilder.append(line);
                line = reader.readLine();
                if (null != line) {
                    responseBuilder.append("\r\n");
                }
            }
            String result = responseBuilder.toString().trim();
            Ret ret = JsonKit.fromJson(result, Ret.class);
            if (ret.isOk()) {
                return URLDecoder.decode(ret.getStr("result"), Charset.UTF8.value);
            } else {
                logger.error("api request forward by urlConnection result: {}", result);
                throw new RuntimeException(ret.getStr("msg"));
            }
        } finally {
            try {
                if (null != reader) {
                    reader.close();
                }
                if (null != urlConnection) {
                    urlConnection.disconnect();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    private HttpURLConnection testCreateConnection(String xmlStr) throws Exception {
        URL httpsUrl = new URL(getUrl());
        HttpURLConnection conn = (HttpURLConnection) httpsUrl.openConnection();
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setRequestMethod("POST");
        conn.setConnectTimeout(20000);
        conn.setReadTimeout(20000);

        try (OutputStream outputStream = new DataOutputStream(conn.getOutputStream())) {
            outputStream.write(xmlStr.getBytes());
            outputStream.flush();
        }
        return conn;
    }
}
